xend: VBD QoS policy bits
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 14 Aug 2009 16:10:11 +0000 (17:10 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 14 Aug 2009 16:10:11 +0000 (17:10 +0100)
Add the ability to define VBD QoS policy in the xend layer.

Consider the following vbd entry:

vbd = [
   'phy:/dev/server/virtualmachine1-disk,xvda1,w,credit=3D5000/s@50ms',
]

This means that a VM may perform 5000 I/O operations per second, with
credit being replenished every 50 milliseconds.

The 'credit' xenstore value is by the blkback driver to ratelimit I/O
operations for the specific device.

Signed-off-by: William Pitcock <nenolod@dereferenced.org>
tools/python/README.sxpcfg
tools/python/xen/xend/server/blkif.py

index 7a17fd1515efa684177a4f80c18d9e3490669080..9c3dce96e890025e10f4ac43867dce8536e0fda6 100644 (file)
@@ -82,6 +82,7 @@ vbd
   - uname
   - dev (ioemu:, .. etc)
   - mode (r, w. w!)
+  - credit
 
 vif
   - type
index 60e162534f16466f1bdda3c378422d65ddde7e24..87d04926f938a62dc662d36e100a750f6763d52e 100644 (file)
@@ -42,6 +42,37 @@ class BlkifController(DevController):
 
         return os.access(auxbin.scripts_dir() + '/block-%s' % protocol, os.X_OK)
 
+    def _calculateRateLimit(self, credstr):
+        """Calculate the rate limit, given a string like: 5000/s@50ms.
+        If this fails, the limit is unlimited.
+        """
+        credit_per_interval = 0xffffffffL
+        interval_usecs = 0L
+
+        credit_re = re.compile("^([0-9]+)/s(@([0-9]+)([mu]?)s)?$")
+
+        m = credit_re.match(credstr)
+        if m:
+            credit_per_sec = m.group(1)
+
+            if m.group(2) is None:
+                interval_usecs = 50000L      # 50ms default
+            else:
+                interval_usecs = long(m.group(5))
+                if m.group(3) == '':
+                    interval_usecs *= 1000 * 1000
+                elif m.group(3) == 'm':
+                    interval_usecs *= 1000
+
+            credit_per_interval = (credit_per_sec * interval_usecs) / 1000000L
+
+            # overflow / underflow checking: default to unlimited rate
+            if credit_per_interval == 0 or credit_per_interval > 0xffffffffL or \
+               interval_usecs == 0 or interval_usecs > 0xffffffffL:
+                credit_per_interval = 0xffffffffL
+                interval_usecs     = 0L
+
+        return "%lu,%lu" % (credit_per_interval, interval_usecs)
 
     def getDeviceDetails(self, config):
         """@see DevController.getDeviceDetails"""
@@ -91,6 +122,10 @@ class BlkifController(DevController):
         if security.on() == xsconstants.XS_POLICY_USE:
             self.do_access_control(config, uname)
 
+        cred = config.get('credit', '')
+        if cred:
+            back['credit'] = self._calculateRateLimit(cred)
+
         (device_path, devid) = blkif.blkdev_name_to_number(dev)
         if devid is None:
             raise VmError('Unable to find number for device (%s)' % (dev))
@@ -153,12 +188,12 @@ class BlkifController(DevController):
         config = DevController.getDeviceConfiguration(self, devid, transaction)
         if transaction is None:
             devinfo = self.readBackend(devid, 'dev', 'type', 'params', 'mode',
-                                       'uuid', 'bootable')
+                                       'uuid', 'bootable', 'credit')
         else:
             devinfo = self.readBackendTxn(transaction, devid,
                                           'dev', 'type', 'params', 'mode', 'uuid',
-                                          'bootable')
-        dev, typ, params, mode, uuid, bootable = devinfo
+                                          'bootable', 'credit')
+        dev, typ, params, mode, uuid, bootable, credit = devinfo
         
         if dev:
             if transaction is None:
@@ -178,6 +213,8 @@ class BlkifController(DevController):
             config['uuid'] = uuid
         if bootable != None:
             config['bootable'] = int(bootable)
+        if credit:
+            config['credit'] = credit
 
         proto = self.readFrontend(devid, 'protocol')
         if proto: